###############################################################################
# CoreDuo
###############################################################################
@CLASS
core
###############################################################################



###############################################################################
@auto[]
$sPagesTableName[pages]
$sAttributesTableName[attributes] ^rem{ имя таблицы со значениями свойств }

# тут у нас будут храниться объекты (все их свойства из базы)
# это хэш с id объекта в качестве ключа
$hPages[^hash::create[]]

# тут у нас будут храниться потомки объектов
# это хэш с id объекта в качестве ключа
$offsprings[^hash::create[]]

# а тут у будут храниться все домены-поддомены-языки
# это хэш с slid сайта в качестве ключа
$hSites[^hash::create[]]

#end @auto[]
###############################################################################



###############################################################################
# конструктор
@init[param][tSiteLang]

$_pSQL[$param.pSQL]

# загрузить структуру доменов-поддоменов-языков в хэш,
# чтобы больше не лазить в БД за ними
$hSites[^_pSQL.hash{
	SELECT
	  languages.id as slid,
	  languages.site as site_id,
	  languages.main as main,
	  languages.suffix as suffix,
	  languages.name as lang_name,
	  languages.title as lang_title,
	  sites.pid as site_pid,
	  sites.url as site_url,
	  sites.name as site_name,
	  sites.title as site_title,
	  IF(languages.suffix != '', CONCAT(sites.url, '/', languages.suffix), sites.url) as url
	FROM sites, languages
	WHERE languages.site = sites.id
}]



# end @init[]
###############################################################################



###############################################################################
# отрезать от uri все параметры и завершающий слэш
@cropURI[uri]
$result[^uri.match[\?.*|[\/]*^$|[\/]*\?.*][]{}]
###############################################################################



###############################################################################
# определение slid по домену и uri
@getSLID[dom;uri][suffix;domain;url;slid;site]
# выделить суффикс (возможно, он языковой)
$suffix[^uri.match[/([^^\/\?]*).*][i]{^if($match.1 ne ''){/}$match.1}]
# отрезать www от имени домена
$domain[^dom.match[^^www\.][i]{}]
# выявить принадлежность uri к какому-либо сайту
$result(false)
^hSites.foreach[slid;site]{
	$url[${domain}$suffix]
	^if($site.url eq $url){
		$result($slid)
		# есть совпадение домен/суффикс - сразу на выход
		^break[]
	}
	^if($site.url eq $domain){
		# если совпадение домена без суффикса - ищем дальше
		$result($slid)
	}
}
^if(!$result){^throw[bad.slid;getSLID;slid not found]}
###############################################################################



###############################################################################
# Получить информацию о странице сайта в виде таблицы (кешируется в хэше)
# Кроме всех полей таблицы страниц из БД, в хэш пишутся поля:
# main(true|false) - является ли данная страница главной у своего сайта
@getObject[id][o_table;dom;uri;slid]

^if(^id.int(0) == 0){
	# id не передан - вернуть свойства текущей страницы
	$dom[^env:SERVER_NAME.match[^^www\.][i]{}]
	$uri[^cropURI[$request:uri]]
	$slid(^getSLID[$dom;$uri])
	$id[^uri2id[$slid;$uri]]
}

^if(!$hPages.[$id]){
	# если данных нет в хэше - получить из БД
	^try{
		$o_table[^_pSQL.table{
				SELECT
					pages.slid,
					IF(pages.id = languages.main, -pages.slid, pages.id) as `id`,   ^rem{ чтобы главные страницы были отрицательными }
					IF(pages.id = languages.main, -pages.slid, pages.pid) as `pid`, ^rem{ чтобы родители главных строниц были тоже отрицательными}
					pages.module,
					pages.template,
					pages.url,
					pages.order,
					pages.name,
					pages.title,
					languages.suffix,
					IF(pages.id = languages.main, 'true', 'false') AS `main`,
					IF(pages.id = languages.main, -1000, pages.order ) AS `order`
				FROM `languages`,`pages`
				WHERE
					pages.id = ^if($id > 0){'$id'}{'$hSites.[^math:abs($id)].main'}
					AND languages.id = pages.slid
			}[$.limit[1]]]
		# бросить таблицу в хэш
		$hPages.[$id][$o_table]
	}{
	  $exception.handled(0)
	}
}
$result[$hPages.[$id]]

# end @getObject[]
###############################################################################



###############################################################################
# возвращает хэш значений аттрибутов объекта
@getAttributes[id;history_step][prop_hash;hAttributes;key;hash;attribute;value;right_id]
^rem{ ID должен быть определен }
^if(^id.int(0) == 0){^throw[bad.id;getAttributes;bad ID '$id']}
$history_step[^history_step.int(0)]
$right_id(^if($id > 0){$id}{$hSites.[^math:abs($id)].main})
$hAttributes[^hash::create[]]

^try{
	^rem{ значения из БД - в хэш }
	^rem{ ключами хэша будут являться названия свойств }

	^if($history_step == 0){ ^rem{
		в данном варианте между sql-сервером и клиентом передаются ТОЛЬКО НЕОБХОДИМЫЕ ДАННЫЕ
		а в версии с выбором шага истории, при выборе последнего шага, передаются все предыдущие }
		$prop_hash[^MAIN:pSQL.hash{
			SELECT attribute, i_value, f_value, v_value, t_value, d_value
			FROM $sAttributesTableName a
			WHERE oid = '$right_id'
				AND dt = (SELECT MAX(dt) FROM $sAttributesTableName WHERE oid = '$right_id' AND attribute = a.attribute)
		}]
	}{ ^rem{
		выбираются ВСЕ ДАННЫЕ, возраст которых больше или равен возрасту указанного шага истории ($history_step)
		самые свежие данные выводятся первыми и, благодаря ( $.distinct(1) ), только они попадают в хэш. }
		$prop_hash[^MAIN:pSQL.hash{
			SELECT attribute, i_value, f_value, v_value, t_value, d_value
		  FROM $sAttributesTableName
		  WHERE oid = '$right_id'
		    AND dt <= (SELECT DISTINCT dt FROM $sAttributesTableName WHERE oid = '$right_id' ORDER BY dt DESC LIMIT $history_step, 1)
		  ORDER BY dt DESC
		}[$.distinct(1)]]
	}

	^if(^prop_hash._count[] > 0){
		^rem{ хэш значений "сливается" в одну переменную }
		$hAttributes[
			^prop_hash.foreach[key;hash]{
				^rem{ объединяются значения всех полей БД (i_value, f_value, t_value, d_value...) }
				^rem{ предполагается, что для одного свойства есть только один тип значения }
				$.$key[^hash.foreach[attribute;value]{$value}]
			}
		]
	}
}{
	$exception.handled(0)
}
$result[$hAttributes]
###############################################################################



###############################################################################
# получить id раздела по домену и uri
@uri2id[slid;uri][id;urisplit;suffix;main]

$uri[^cropURI[$uri]]
$suffix[$hSites.$slid.suffix]
$main($hSites.$slid.main)

# отрезать языковой суффикс (для поиска по таблице страниц)
$uri[^uri.match[^^\/$suffix][i]{}]

$id(-$slid)
$urisplit[^uri.split[/;l]]
^urisplit.menu{
	^if($urisplit.piece ne ''){ $id(^findSection[$urisplit.piece;$id]) }
}

# страница, назначенная главной, не найдется по своему настоящему URI
^if($id == $main){$id(-$slid)}

$result($id)

# end @uri2id[]
###############################################################################



###############################################################################
# получить id раздела по его имени и id родителя
@findSection[url;pid]
$result(^_pSQL.int{SELECT id FROM $sPagesTableName WHERE pid  = '$pid' AND url  = '$url'}[$.default(0)])
# end @findSection[]
###############################################################################



###############################################################################
# получить абсолютный путь до раздела по его id
@id2uri[id][cur_uri;cur_id;tObjectInfo;dom;uri;slid;main;suffix]

$tObjectInfo[^getObject[$id]]

^if(!def $tObjectInfo.id){^throw[bad.id;id2uri;bad ID '$id', page not found]}

$slid[$tObjectInfo.slid]
$main($hSites.$slid.main)
$suffix[$hSites.$slid.suffix]

# для id страницы, назначенной главной, выдается URI главной
^if($id < 0 || $id == $main){
	$cur_uri[]
}{
	# для остальных всё штатно
	$cur_id[$id]
	$cur_uri[]

	^while($cur_id > 0){
	  # получить информацию об объекте
		$tObjectInfo[^getObject[$cur_id]]
		# установить новый id
		$cur_id[$tObjectInfo.pid]

		^if($cur_uri eq ''){
			$cur_uri[$tObjectInfo.url]
		}{
			$cur_uri[$tObjectInfo.url/$cur_uri]
		}
	}

	# если путь не является файлом, то добавить заключительный слэш
	^if($cur_uri ne ''){
		$cur_uri[^cur_uri.match[^^([^^?|^^.]+[^^/])^$][]{$match.1/}]
	}

}

# добавить языковой суффикс
$result[^if($suffix ne ''){/$suffix}/$cur_uri]

# end @id2uri[]
###############################################################################




###############################################################################
# возвращает таблицу, в которой последовательно перечислены
# все ссылки, входящие в навигацию "хлебные крошки" для данного ID
@getBreadCrumb[id][uri;cur_id;tObjectInfo;tab]

$cur_id[$id]
$uri[]

$tab[]
^while(1==1){
  ^rem{ получить информацию об объекте }
	$tObjectInfo[^getObject[$cur_id]]
	^rem{ обновить текущий id }
	$cur_id[$tObjectInfo.pid]

  $tab[$tObjectInfo.id	^id2uri[$tObjectInfo.id]	$tObjectInfo.main	$tObjectInfo.name	$tObjectInfo.title
$tab]

	^if($tObjectInfo.main eq 'true'){^break[]}
}

	$result[^table::create{id	link	main	name	title
$tab}]

#end @getBreadCrumb[]
###############################################################################



###############################################################################
# получить информацию о прямых потомках, которые доступны на чтение
# результат кладется в хэш. при повторном обращении берется оттуда.
# возвращается таблица с ключами как в БД
@getOffsprings[id][o_table;tObjectInfo]


^if(!$offsprings.[$id]){
	^try{

		$o_table[^_pSQL.table{
				SELECT
					pages.slid,
					IF(pages.id = languages.main, -pages.slid, pages.id) as `id`,
					IF(pages.pid = languages.main, -pages.slid, pages.pid) as `pid`,
					pages.module,
					pages.template,
					pages.url,
					pages.order,
					pages.name,
					pages.title,
					languages.suffix,
					IF(pages.id = languages.main, 'true', 'false') AS `main`,
					IF(pages.id = languages.main, -1000, pages.order ) AS `order`
				FROM `languages`,`pages`
				WHERE
					pages.pid = '$id' AND	languages.id = pages.slid AND	(
						pages.id != languages.main OR     ^rem{ не выводить в потомках страницы, которые назначены главными }
						pages.pid < 0	AND pages.id = languages.main    ^rem{ но выводить главные в навигации первого уровня }
					)
				ORDER BY `order`
		}]

		^rem{ бросить в хэш }
		$offsprings.[$id][$o_table]
    ^rem{ заодно добавить записи в хэш объектов }
    ^hPages.add[^o_table.hash[id]]
	}{
	  ^rem{ объект не найден }
	  $exception.handled(0)
	}
}

$result[$offsprings.[$id]]

###############################################################################












###############################################################################
# добавить страницу
@pageAdd[pid;module;template;url;name;title;order;tObjectInfo]

$order(^order.int(0))   ^rem{ если не определен порядок, то 0 }
$pid(^pid.int(0))

^if( $pid == 0 || ^module.length[] < 1 || ^template.length[] < 1 || ^url.length[] < 1 || ^name.length[] < 1 ){
	^rem{ если данных мало, вызвать ошибку }
	^throw[loData;pageAdd;недостаточно данных передано в добавлялку страницы $pid $module $template $dir $name $title $order]
}

$tObjectInfo[^getObject[$pid]]

^_pSQL.void{
	INSERT INTO $sPagesTableName (`slid`, `pid`, `module`, `template`, `url`, `order`, `name`, `title`)
	VALUES ('$tObjectInfo.slid', '$pid', '$module', '$template', '$url', '$order', '$name', '$title')
}
$result(^_pSQL.last_insert_id[$sPagesTableName])

# end @pageAdd[]
###############################################################################



###############################################################################
# редактировать страницу
@pageEdit[id;module;template;url;name;title]

$id(^id.int(0))

^if(!$id){ ^throw[wrongID;pageEdit;Не задан ID редактируемой страницы] }

^try{
	^_pSQL.void{
		UPDATE $sPagesTableName SET
			^if(^module.length[] > 0){`module` = '$module',}
			^if(^template.length[] > 0){`template` = '$template',}
			^if(^name.length[] > 0){`name` = '$name',}
			^if(^title.length[] > 0){`title` = '$title',}
			^if(^url.length[] > 0){`url` = '$url',}
			`id` = `id`
		WHERE `id` = ^if($id > 0){'$id'}{'$hSites.[^math:abs($id)].main'}
	}[$.limit(1)]
	$result(true)
}{
	$exception.handled(true)
	$result(false)
}
# end @pageEdit[]
###############################################################################



###############################################################################
# переместить страницу
@pageMove[id;pid;order]

$id(^id.int(0))
$pid(^pid.int(0))
$order(^order.int(-666))

^if(!$id){ ^throw[wrongID;pageMove;Не задан ID редактируемой страницы] }
^if(!$pid || $order == -666){ ^throw[loData;pageMove;Недостаточно данных] }

^try{
	^_pSQL.void{
		UPDATE $sPagesTableName SET
			^if($pid){`pid` = '$pid',} `order` = '$order'
		WHERE `id` = '$id'
	}[$.limit(1)]

	^rem{ "нормировать" order }
	$res[^normalizeTread[$pid]]

	$result(true)
}{
	$exception.handled(0)
	$result(false)
}
# end @pageMove[]
###############################################################################



###############################################################################
# копировать страницу
# id - источник
# pid - приемник
# order - порядок
@pageCopy[id;pid;order][tOffsprings;newObjectID;tObjectInfo;hObjectAttributes;sValues;right_id]

$id(^id.int(0))
$pid(^pid.int(0))
$order(^order.int(-666))

^if(!$id){ ^throw[wrongID;pageCopy;Не задан ID редактируемой страницы] }
^if(!$pid || $order == -666){ ^throw[loData;pageCopy;Недостаточно данных] }


# свойства исходной страницы
$tObjectInfo[^getObject[$id]]
# потомки исходной страницы
$tOffsprings[^getOffsprings[$id]]

# вставить копию в таблицу и получить ID новой страницы
^_pSQL.void{
	INSERT INTO $sPagesTableName (`slid`, `pid`, `module`, `template`, `url`, `order`, `name`, `title`)
	VALUES ('$tObjectInfo.slid', '$pid', '$tObjectInfo.module', '$tObjectInfo.template', '$tObjectInfo.url', '$order', '$tObjectInfo.name', '$tObjectInfo.title')
}
$newObjectID(^_pSQL.last_insert_id[$sPagesTableName])

^rem{ "нормировать" order }
$res[^normalizeTread[$pid]]


# копировать все атрибуты исходной страницы
# история не копируется
$hObjectAttributes[^getAttributes[$id]]

^rem{ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! }
$right_id[$newObjectID]

$sValues[
	^hObjectAttributes.foreach[key;value]{
		('', '$right_id', '$key', NOW(), NULL, NULL, NULL, '$value', NULL)
	}[, ]
]
$sValues[^sValues.trim[]]

^if(def $sValues){
	^MAIN:pSQL.void{
		INSERT INTO $sAttributesTableName (`class`,`oid`,`attribute`,`dt`,`i_value`,`f_value`,`v_value`,`t_value`,`d_value`)
		VALUES $sValues
	}
}


^rem{ рекурсивно скопировать потомков }
^tOffsprings.menu{
	^pageCopy[$tOffsprings.id;$newObjectID;$tOffsprings.order]
}

# end @pageCopy[]
###############################################################################



###############################################################################
# "нормировать" ветку (сделать order последовательным, от 1)
@normalizeTread[pid][sTableID]
$pid(^pid.int(0))
^if(!$pid){ ^throw[loData;normalizeTread;Не задан pid] }
# имя временной таблицы
$sTableID[tmp_^math:uid64[]]
^try{
	^_pSQL.void{
		CREATE TEMPORARY TABLE `$sTableID` (
		  `slid` int(8) NOT NULL,
		  `id` int(11) NOT NULL,
		  `pid` int(11) NOT NULL,
		  `module` varchar(255) NOT NULL,
		  `template` varchar(255) NOT NULL,
		  `url` varchar(255) NOT NULL,
		  `order` int(11) NOT NULL auto_increment,
		  `name` varchar(255) NOT NULL,
		  `title` varchar(255) NOT NULL,
		  PRIMARY KEY  (`order`)
		)
	}
	^_pSQL.void{
		INSERT INTO `$sTableID`
		  SELECT slid, id, pid, module, template, url, NULL, name, title
		  FROM `pages` WHERE `pid` = '$pid' ORDER BY `order`
	}
	^_pSQL.void{REPLACE INTO `pages` SELECT * FROM `$sTableID`}
	$result(true)
}{
	$exception.handled(0)
	$result(false)
}
###############################################################################



###############################################################################
# удалить страницу
# рекурсивно удаляет страницу, все подстраницы и их свойства
@pageRemove[id;off][tOffsprings;tObjectInfo]

$id(^id.int(-1))
$off(^off.bool(false))
$result(false)

^if($id > 0){
	$tOffsprings[^getOffsprings[$id]]
	$tObjectInfo[^getObject[$id]]
	^_pSQL.void{ DELETE FROM $sPagesTableName WHERE `id` = $id LIMIT 1 }
	^_pSQL.void{ DELETE FROM $sAttributesTableName WHERE `oid` = $id }
	^rem{ рекурсивно удалить потомков }
	^tOffsprings.menu{
		$res[^pageRemove[$tOffsprings.id;true]]
	}
	^rem{ "нормировать" ветку родителя удаленного элемента }
	^if(!$off){
		$res[^normalizeTread[$tObjectInfo.pid]]
	}
	$result(true)
}

# end @pageRemove[]
###############################################################################



###############################################################################
# изменить порядок
@pageOrder[id]


# end @pageOrder[]
###############################################################################




